#include "cplayer.h"
#include <cstdlib>

namespace reversi
{

CPlayer::CPlayer()
{
    ownSideValue = 0;
    otherSideValue = 0;
    cornerValue = 1000;
    initDepth = 5;
    maxDepth = 65;
    winningBoard = INT_MAX - 5;
    losingBoard = INT_MIN + 5;
    ownLeft = 100;
    otherLeft = 100;
}

void CPlayer::Initialize(bool pFirst,const CDeadline &pDue)
{
    srand(CDeadline::Now());
    //You can use this function to perform initialization tasks for your program. It is
    //perfectly ok to leave it as is
}
    
CMove CPlayer::Play(const CBoard &pBoard,const CDeadline &pDue)
{
    CMove ret, r;
    int depth = initDepth;
    tLeft = pDue;
    //Use the commented version if your system supports ANSI color (linux does)
//    pBoard.PrintNoColor();
    //pBoard.Print();
    
    //Iterative depening alpha-beta pruning until i realise there is no more
    //to go down another level
    clock_t st = clock();
    while(depth < maxDepth)
    {
//        std::cout << "Trying depth " << depth << std::endl;
        r = minmax(pBoard, depth);
        if((r.Row() == -1) && (r.Who() == OTHER))
            break;

        ret = r;
        ++ depth;
    }

//    std::cout << "Returning in " << (clock() - st) / (CLOCKS_PER_SEC / 1000) << " mseconds" << std::endl;
    if((clock() - st) / (CLOCKS_PER_SEC / 1000) > 2000)
    {
        std::cout << "Timed out" << std::endl;
        exit(0);
    }

//    std::cout << "Move returned: " << ret.Row() << ", " << ret.Col() << ". On depth: " << depth - 1 << std::endl;
    return ret;
}

CMove CPlayer::minmax(CBoard b, int depth)
{
    std::vector<CMove> valid;
    CMove best;
    int alpha = INT_MIN, beta = INT_MAX;
    b.FindPossibleMoves(valid, OWN);

    std::vector<CMove>::iterator i;
    
    //If no moves can be made, then send a pass
    if(valid.empty())
    {
//        std::cout << "Returning a pass" << std::endl;
        return *(new CMove(-1, -1, OWN));
    }       

    //Look for the best move to return
    for(i = valid.begin(); i != valid.end(); ++ i)
    {
        if(tLeft.RemainingUntil() < 100)
        {
            if(depth == initDepth)
                return best;
            return *(new CMove(-1, -1, OTHER));
        }
        CBoard *newBoard = new CBoard(b, *i);
        int moveEval = minimize(*newBoard, depth, alpha, beta, false);


        if(moveEval > alpha)
        {
            alpha = moveEval;
            best = *i;
        }else
        {
            delete newBoard;
        }
    }

    return best;
}

int CPlayer::maximize(CBoard b, int depth, int alpha, int beta, bool isPass)
{
    std::vector<CMove> valid;
    std::vector<CMove>::iterator i;
    if(depth == 0)
        return boardEval(b);
    
    b.FindPossibleMoves(valid, OWN);

    //If no moves can be made, then check if the game is over
    if(valid.empty())
    {
        //The game is not over, so this is a pass, call minimize with the same
        //board
        if(b.OwnCount() + b.OtherCount() != 64)
        {
            //The other player also passed, so the game is over
            if(isPass)
            {
                if(b.OwnCount() > b.OtherCount())
                {
                    return winningBoard;
                }else
                {
                    return losingBoard;
                }
            }
            //If the other player didn't pass, then send him control
            alpha = minimize(b, depth - 1, alpha, beta, true);
        }else
        {
            //End game situation
            //All pieces have been placed
            if(b.OwnCount() > b.OtherCount())
            {
                return winningBoard;
            }else
            {
                return losingBoard;
            }
        }
     }       

    for(i = valid.begin(); i != valid.end(); ++ i)
    {
        //Abort if there is not enough time
        if(tLeft.RemainingUntil() < 100)
        {
//            std::cout << "Max: Out of time(" << tLeft.RemainingUntil() << "). On depth " << depth << std::endl;
            return alpha;
        }

        CBoard *newBoard = new CBoard(b, *i);
        int moveEval = minimize(*newBoard, depth - 1, alpha, beta, false);
        delete newBoard;

        if(moveEval > alpha)
        {
            alpha = moveEval;
        }
        if(beta < alpha)
            break; //Beta pruning
    }

    return alpha;
}

int CPlayer::minimize(CBoard b, int depth, int alpha, int beta, bool isPass)
{
    std::vector<CMove> valid;
    std::vector<CMove>::iterator i;
    
    if(depth == 0)
        return boardEval(b);

    b.FindPossibleMoves(valid, OTHER);

    //If no moves can be made, then check if the game is over
    if(valid.empty())
    {
        //The game is not over, so this is a pass, call minimize with the same
        //board
        if(b.OwnCount() + b.OtherCount() != 64)
        {
            //The other player also passed, so the game is over
            if(isPass)
            {
                if(b.OwnCount() > b.OtherCount())
                {
                    return winningBoard;
                }else
                {
                    return losingBoard;
                }
            }
            //If the other player didn't pass, then send him control
            alpha = maximize(b, depth - 1, alpha, beta, true);
        }else
        {
            //End game situation
            //All pieces have been placed
            if(b.OwnCount() + b.OtherCount() == 64)
            {
                if(b.OwnCount() > b.OtherCount())
                {
                    return winningBoard;
                }else
                {
                    return losingBoard;
                }
            }
        }
     }       


    for(i = valid.begin(); i != valid.end(); ++ i)
    {
        //Abort if there is not enough time
        if(tLeft.RemainingUntil() < 100)
        {
//           std::cout << "Min: Out of time(" << tLeft.RemainingUntil() << "). On depth " << depth << std::endl;
           return beta;
        }

        CBoard *newBoard = new CBoard(b, *i);
        int moveEval = maximize(*newBoard, depth - 1, alpha, beta, false);
        delete newBoard;

        if(moveEval < beta)
        {
            beta = moveEval;
        }
        if(beta < alpha)
            break; //Alpha pruning
    }

    return beta;
}

int CPlayer::boardEval(CBoard b)
{
    int val = 0, chipsOwn = 0, chipsOther = 0;
    std::vector<CMove> moves;

    //Count the number of corners owned by each player
    if(b.At(0, 0) != EMPTY)
        val += cornerValue * (b.At(0, 0) == OWN ? 1 : -1);
    if(b.At(0, 63) != EMPTY)
        val += cornerValue * (b.At(0, 63) == OWN ? 1 : -1);
    if(b.At(63, 0) != EMPTY)
        val += cornerValue * (b.At(63, 0) == OWN ? 1 : -1);
    if(b.At(63, 63) != EMPTY)
        val += cornerValue * (b.At(63, 63) == OWN ? 1 : -1);

    
    //Count the number of sides owned by each player
    if((ownSideValue != 0) || (otherSideValue != 0))
        for(int i = 1; i < 63; ++ i)
        {
            if(b.At(0, i) != EMPTY)
                val += (b.At(0, i) == OWN ? ownSideValue : -otherSideValue);
            if(b.At(63, i) != EMPTY)
                val += (b.At(63, i) == OWN ? ownSideValue : -otherSideValue);
            if(b.At(i, 0) != EMPTY)
                val += (b.At(0, i) == OWN ? ownSideValue : -otherSideValue);
            if(b.At(i, 63) != EMPTY)
                val += (b.At(0, i) == OWN ? ownSideValue : -otherSideValue);
        }

    //Assign a value to the number of chips left
    b.FindPossibleMoves(moves, OWN);
    val += ownLeft * moves.size();
    b.FindPossibleMoves(moves, OTHER);
    val += (-1) * otherLeft * moves.size();

    //Add the difference of chips
    val += b.OwnCount() + b.OtherCount();

    //If it is not a final board, assign values to the chips
    return val;
}
/*namespace reversi*/ }
